#include "esp_log.h"
#include "w2p_auto_wifi.h"

/* FreeRTOS event group to signal when we are connected & ready to make a request */
static EventGroupHandle_t s_wifi_event_group;

/* The event group allows multiple bits for each event,
   but we only care about one event - are we connected
   to the AP with an IP? */
static const int CONNECTED_BIT = BIT0;
static const int ESPTOUCH_DONE_BIT = BIT1;
static const char *TAG = "AUTO_WIFI";



static int s_retry_num = 0;
#define EXAMPLE_MAXIMUM_RETRY      10



void _AutoWifi_event_handler(void* arg, esp_event_base_t event_base,
                                int32_t event_id, void* event_data)
{
    if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
            xTaskCreatePinnedToCore(_AutoWifi_smartconfig_task, "_AutoWifi_smartconfig_task", 4096, NULL, 10, NULL,0);
        // xTaskCreate(_AutoWifi_smartconfig_task, "_AutoWifi_smartconfig_task", 4096, NULL, 3, NULL);
    } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_CONNECTED) {
      ESP_LOGI(TAG, "Wi-Fi connected");
    }
    else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
        appConfig.hasConnected=false;
        esp_wifi_connect();
        xEventGroupClearBits(s_wifi_event_group, CONNECTED_BIT);
    } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {        
        appConfig.hasConnected=true;

        s_retry_num = 0;
        ip_event_got_ip_t *event_ip = (ip_event_got_ip_t *)event_data;
        
        char tmpIp[ip_addr_char_length()];
        ip_addr_to_str(&event_ip->ip_info.ip,tmpIp);        
        strcpy(appConfig.ipAddr,tmpIp);
        ESP_LOGI(TAG, "Got IP: " IPSTR, IP2STR(&event_ip->ip_info.ip));
        ESP_LOGI(TAG," assign ip : %s",appConfig.ipAddr);
        xEventGroupSetBits(s_wifi_event_group, CONNECTED_BIT);
        
      xTaskCreatePinnedToCore(auto_notify_online_multicast_task, "notify_online_udp", 4096, NULL, 10, NULL,0);
      xTaskCreatePinnedToCore(auto_tcp_server_control_task, "tcp_control_center", 4096, NULL, 10, NULL,0);

    } else if (event_base == SC_EVENT && event_id == SC_EVENT_SCAN_DONE) {
        ESP_LOGI(TAG, "Scan done");
    } else if (event_base == SC_EVENT && event_id == SC_EVENT_FOUND_CHANNEL) {
        ESP_LOGI(TAG, "Found channel");
    } else if (event_base == SC_EVENT && event_id == SC_EVENT_GOT_SSID_PSWD) {
        ESP_LOGI(TAG, "Got SSID and password");

        smartconfig_event_got_ssid_pswd_t *evt = (smartconfig_event_got_ssid_pswd_t *)event_data;
        wifi_config_t wifi_config;
        uint8_t ssid[33] = { 0 };
        uint8_t password[65] = { 0 };
        uint8_t rvd_data[33] = { 0 };

        bzero(&wifi_config, sizeof(wifi_config_t));
        memcpy(wifi_config.sta.ssid, evt->ssid, sizeof(wifi_config.sta.ssid));
        memcpy(wifi_config.sta.password, evt->password, sizeof(wifi_config.sta.password));

#ifdef CONFIG_SET_MAC_ADDRESS_OF_TARGET_AP
        wifi_config.sta.bssid_set = evt->bssid_set;
        if (wifi_config.sta.bssid_set == true) {
            ESP_LOGI(TAG, "Set MAC address of target AP: "MACSTR" ", MAC2STR(evt->bssid));
            memcpy(wifi_config.sta.bssid, evt->bssid, sizeof(wifi_config.sta.bssid));
        }
#endif

        memcpy(ssid, evt->ssid, sizeof(evt->ssid));
        memcpy(password, evt->password, sizeof(evt->password));
        ESP_LOGI(TAG, "SSID:%s", ssid);
        ESP_LOGI(TAG, "PASSWORD:%s", password);
        if (evt->type == SC_TYPE_ESPTOUCH_V2) {
            ESP_ERROR_CHECK( esp_smartconfig_get_rvd_data(rvd_data, sizeof(rvd_data)) );
            ESP_LOGI(TAG, "RVD_DATA:");
            for (int i=0; i<33; i++) {
                printf("%02x ", rvd_data[i]);
            }
            printf("\n");
        }

        ESP_ERROR_CHECK( esp_wifi_disconnect() );
        ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );
        esp_wifi_connect();
    } else if (event_base == SC_EVENT && event_id == SC_EVENT_SEND_ACK_DONE) {
        xEventGroupSetBits(s_wifi_event_group, ESPTOUCH_DONE_BIT);
    }
}

void _AutoWifi_initialise_wifi(void)
{
    ESP_ERROR_CHECK(esp_netif_init());
    s_wifi_event_group = xEventGroupCreate();
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta();
    assert(sta_netif);

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK( esp_wifi_init(&cfg) );

    ESP_ERROR_CHECK( esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &_AutoWifi_event_handler, NULL) );
    ESP_ERROR_CHECK( esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &_AutoWifi_event_handler, NULL) );
    ESP_ERROR_CHECK( esp_event_handler_register(SC_EVENT, ESP_EVENT_ANY_ID, &_AutoWifi_event_handler, NULL) );

//-- try fix: begin
    ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_NONE)); // default is WIFI_PS_MIN_MODEM
    ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); // default is WIFI_STORAGE_FLASH
//-- try fix: end    
    ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );

        uint8_t mac[6];
    // 获取WiFi Station模式下的MAC地址
    esp_wifi_get_mac(ESP_IF_WIFI_STA, mac);

    // 打印MAC地址
    // ESP_LOGI(TAG, "WiFi Station MAC address: %02x:%02x:%02x:%02x:%02x:%02x", 
    //          mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
    static char macStr[18];  // 用于存储 IP 地址字符串，IPv4 地址最长为 15 个字符，加上 '\0' 共 16 个字符
    snprintf(macStr, sizeof(macStr), "%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);    
    appConfig.macAddr=macStr;
    ESP_LOGI(TAG, "WiFi Station MAC address: %s", 
             appConfig.macAddr);
    ESP_ERROR_CHECK( esp_wifi_start() );
    
}

void _AutoWifi_smartconfig_task(void * parm)
{
    EventBits_t uxBits;
    ESP_ERROR_CHECK( esp_smartconfig_set_type(SC_TYPE_ESPTOUCH) );
    smartconfig_start_config_t cfg = SMARTCONFIG_START_CONFIG_DEFAULT();
    ESP_ERROR_CHECK( esp_smartconfig_start(&cfg) );
    while (1) {
        uxBits = xEventGroupWaitBits(s_wifi_event_group, CONNECTED_BIT | ESPTOUCH_DONE_BIT, true, false, portMAX_DELAY);
        if(uxBits & CONNECTED_BIT) {
            ESP_LOGI(TAG, "WiFi Connected to ap");

        }
        //--try fix:  能够一直配网 不停止。begin
        if(uxBits & ESPTOUCH_DONE_BIT) {
            ESP_LOGI(TAG, "smartconfig over");
            esp_smartconfig_stop();
            vTaskDelete(NULL);
            
        }
        //--try fix:  能够一直配网 不停止。end        
    }
}

void auto_wifi_app_main(void)
{
    
    _AutoWifi_initialise_wifi();
}


//-- 组播 在线状态 begin
// #define MULTICAST_IP "239.255.255.250"
#define MULTICAST_IP "239.0.0.1"
#define MULTICAST_PORT 22444
static int _send_multicast_count=0;
void auto_notify_online_multicast_task(void *pvParameters)
{

int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
    if (sock < 0) {
        ESP_LOGE("UDP", "Failed to create socket");
        return;
    }

    int optval = 1;
    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));

    struct sockaddr_in localAddr;
    memset(&localAddr, 0, sizeof(localAddr));
    localAddr.sin_family = AF_INET;
    localAddr.sin_port = htons(MULTICAST_PORT);
    localAddr.sin_addr.s_addr = htonl(INADDR_ANY);

    if (bind(sock, (struct sockaddr *)&localAddr, sizeof(localAddr)) < 0) {
        ESP_LOGE("UDP", "Failed to bind socket");
        close(sock);
        return;
    }

    struct ip_mreq mreq;
    inet_pton(AF_INET, MULTICAST_IP, &mreq.imr_multiaddr.s_addr);
    mreq.imr_interface.s_addr = htonl(INADDR_ANY);

    if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
        ESP_LOGE("UDP", "Failed to join multicast group");
        close(sock);
        return;
    }
    struct sockaddr_in multicastAddr;
    memset(&multicastAddr, 0, sizeof(multicastAddr));
    multicastAddr.sin_family = AF_INET;
    multicastAddr.sin_port = htons(MULTICAST_PORT);
    inet_pton(AF_INET, MULTICAST_IP, &multicastAddr.sin_addr.s_addr);

    char * json_str = NULL;
    while (1) {
  
      // 获取自系统启动以来的微秒数
    int64_t timestamp_us = esp_timer_get_time();
    // ESP_LOGI(TAG, "Timestamp in microseconds since boot: %lld", timestamp_us);
    // 将微秒转换为毫秒
    int64_t timestamp_ms = timestamp_us / 1000;
    appConfig.time=timestamp_ms;
     json_str = getAppConfigJson();
        int err=sendto(sock, json_str, strlen(json_str), 0, (struct sockaddr *)&multicastAddr, sizeof(multicastAddr));        
        if (err < 0) {
            ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
        } else {
            _send_multicast_count++;
            if(_send_multicast_count%3==0){
                // ESP_LOGI(TAG, " sent to multicast and notify online. %s",json_str);
            }
            if(_send_multicast_count>3){
                _send_multicast_count=0;
            }
            // ESP_LOGI(TAG, "Data sent to multicast group : %lld",timestamp_ms);
        }
        vTaskDelay(pdMS_TO_TICKS(3000)); // 每2秒发送一次数据
    }
    if (sock != -1) {
        ESP_LOGE(TAG, "Shutting down socket and restarting...");
        shutdown(sock, 0);
        close(sock);
    }
       // 释放资源
    free(json_str);
    vTaskDelete(NULL);


    //--end

}
//-- 组播 在线状态 end


//-- tcp 芯片控制 begin
#define PORT 8080
#define TCP_SERVER_TAG "TCP_SERVER"
void auto_tcp_server_control_task(void *pvParameters)
{
    char addr_str[128];
    int listen_sock = -1, new_socket = -1;
    struct sockaddr_in dest_addr;
    // struct sockaddr_in6 dest_addr6;
    struct sockaddr_storage source_addr; // Large enough for both IPv4 or IPv6
    socklen_t addr_len = sizeof(source_addr);

    // 创建套接字
    // if (esp_ip6_addr_is_set()) {
    //     memset(&dest_addr6, 0, sizeof(dest_addr6));
    //     dest_addr6.sin6_family = AF_INET6;
    //     dest_addr6.sin6_port = htons(PORT);
    //     dest_addr6.sin6_addr = in6addr_any;
    //     listen_sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_IP);
    // } else {
    //     memset(&dest_addr, 0, sizeof(dest_addr));
    //     dest_addr.sin_family = AF_INET;
    //     dest_addr.sin_port = htons(PORT);
    //     dest_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    //     listen_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
    // }
        memset(&dest_addr, 0, sizeof(dest_addr));
        dest_addr.sin_family = AF_INET;
        dest_addr.sin_port = htons(PORT);
        dest_addr.sin_addr.s_addr = htonl(INADDR_ANY);
        listen_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
    if (listen_sock < 0) {
        ESP_LOGE(TCP_SERVER_TAG, "Unable to create socket: errno %d", errno);
        vTaskDelete(NULL);
        return;
    }
    ESP_LOGI(TCP_SERVER_TAG, "Socket created");

    // 绑定地址和端口
    int err = bind(listen_sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
    if (err != 0) {
        ESP_LOGE(TCP_SERVER_TAG, "Socket unable to bind: errno %d", errno);
        close(listen_sock);
        vTaskDelete(NULL);
        return;
    }
    ESP_LOGI(TCP_SERVER_TAG, "Socket bound, port %d", PORT);

    // 监听连接
    err = listen(listen_sock, 5);
    if (err != 0) {
        ESP_LOGE(TCP_SERVER_TAG, "Error occurred during listen: errno %d", errno);
        close(listen_sock);
        vTaskDelete(NULL);
        return;
    }
    ESP_LOGI(TCP_SERVER_TAG, "Socket listening");


    while (1) {
        appConfig.tcpLinked=false;
        // 接受连接
        new_socket = accept(listen_sock, (struct sockaddr *)&source_addr, &addr_len);        
        if (new_socket < 0) {
            ESP_LOGE(TCP_SERVER_TAG, "Unable to accept connection: errno %d", errno);
            continue;
        }

        // 获取客户端地址信息
        if (source_addr.ss_family == PF_INET) {
            inet_ntoa_r(((struct sockaddr_in *)&source_addr)->sin_addr, addr_str, sizeof(addr_str) - 1);
        } else if (source_addr.ss_family == PF_INET6) {
            inet6_ntoa_r(((struct sockaddr_in6 *)&source_addr)->sin6_addr, addr_str, sizeof(addr_str) - 1);
        }
        ESP_LOGI(TCP_SERVER_TAG, "Socket accepted ip address: %s", addr_str);
        // 设置接收超时时间为 5 秒
        struct timeval timeout;
        timeout.tv_sec = 5;
        timeout.tv_usec = 0;
        err = setsockopt(new_socket, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
        if (err < 0) {
        ESP_LOGE(TAG, "Error setting receive timeout: errno %d", errno);            
        if (new_socket != -1) {
            ESP_LOGI(TCP_SERVER_TAG, "Shutting down socket and restarting...");
            shutdown(new_socket, 0);
            close(new_socket);
        }
            continue;
        }        
        // 处理数据
        while (1) {
            char rx_buffer[128];
            int len = recv(new_socket, rx_buffer, sizeof(rx_buffer) - 1, 0);
            if (len < 0) {
                ESP_LOGE(TCP_SERVER_TAG, "Error occurred during receiving: errno %d", errno);
                break;
            } else if (len == 0) {
                ESP_LOGI(TCP_SERVER_TAG, "Connection closed");
                break;
            } else {
            appConfig.tcpLinked=true;
                // 在 ESP - IDF 服务器代码中
                cJSON *cmdJson=NULL;
                ESP_LOGI(TCP_SERVER_TAG, "Received %d bytes: %s", len, rx_buffer);
                if(rx_buffer[0]=='{'){
                    //--
                    ESP_LOGI(TCP_SERVER_TAG,"JSON 命令格式");
                    // 解析 JSON 字符串
                        cmdJson= cJSON_Parse(rx_buffer);

                }
                char * opCode=OP_CODE_SUCCESS;
                char *cmdType=NULL;
                if(cmdJson!=NULL){

                    // 获取 "name" 键对应的值
                    cJSON *cmd_item = cJSON_GetObjectItem(cmdJson, "cmd");
                    
                    if (cmd_item != NULL && cJSON_IsString(cmd_item)) {
                        cmdType = cJSON_GetStringValue(cmd_item);
                    } else {
                        opCode=OP_CODE_LACK_COMMAND;
                    }


                }else{
                    opCode=OP_CODE_NOT_JSON_FORMAT;
                }

                if(cmdType==NULL){
                    cJSON *  retJson=cJSON_CreateObject(); 
                    cJSON_AddStringToObject(retJson, "code", opCode);  
                    char * retMsg=cJSON_PrintUnformatted(retJson);
                    int len = strlen(retMsg);
                    int err = send(new_socket, retMsg, len, 0);
                    if (err < 0) {
                    ESP_LOGE(TCP_SERVER_TAG, "Error occurred during sending: errno %d", errno);
                    } else {
                    ESP_LOGI(TCP_SERVER_TAG, "Data sent successfully");
                    }
                    free(retMsg);
                    cJSON_Delete(retJson);
                }else{

                //--## handlers and cmds begin
                char respCode[64];
                
                if(strcasecmp(cmdType,"start-hid")==0){
                    _handle_start_hid(cmdType,cmdJson,respCode);                       
                }else  if(strcasecmp(cmdType,"hid-touch")==0){
                    _handle_hid_touch(cmdType,cmdJson,respCode);                       
                }else  if(strcasecmp(cmdType,"hid-release")==0){
                    _handle_hid_release(cmdType,cmdJson,respCode);                       
                }else  if(strcasecmp(cmdType,"hid-tap")==0){
                    _handle_hid_tap(cmdType,cmdJson,respCode);                       
                }else{
                        // 清空toStr
                        // 使用strcpy进行复制
                    strcpy(respCode, OP_CODE_OUT_RANGE);
                }

                //--## handlers and cmds end
                cJSON *  retJson=cJSON_CreateObject(); 
                cJSON_AddStringToObject(retJson, "code", respCode);  
                char * retMsg=cJSON_PrintUnformatted(retJson);
                int len = strlen(retMsg);
                int err = send(new_socket, retMsg, len, 0);
                if (err < 0) {
                ESP_LOGE(TCP_SERVER_TAG, "Error occurred during sending: errno %d", errno);
                } else {
                ESP_LOGI(TCP_SERVER_TAG, "Data sent successfully");
                }
                free(retMsg);
                cJSON_Delete(retJson);     

                }
                
            }
        }

        if (new_socket != -1) {
            ESP_LOGI(TCP_SERVER_TAG, "Shutting down socket and restarting...");
            shutdown(new_socket, 0);
            close(new_socket);
        }
        appConfig.tcpLinked=false;
    }
    close(listen_sock);
    vTaskDelete(NULL);
}
//-- tcp 芯片控制 end

//--蓝牙+hid begin
void _handle_start_hid(char * cmdType,cJSON *cmdJson,char * retCode){
    if(cmdJson==NULL){
        strcpy(retCode,OP_CODE_PARA_ERR);
        // copy_const_chars(OP_CODE_PARA_ERR,retCode);
        return;
    }
    ESP_LOGI(TAG,"handle - %s",cmdType);
     cJSON *cmd_nick = cJSON_GetObjectItem(cmdJson, "nick");
     cJSON *cmd_code = cJSON_GetObjectItem(cmdJson, "code");

    char * _nick=NULL;
    char * _code=NULL;
                        
    if (cmd_nick != NULL && cJSON_IsString(cmd_nick)) {
    _nick = cJSON_GetStringValue(cmd_nick);            
    } else {    
        strcpy(retCode,OP_CODE_PARA_ERR);
    // copy_const_chars(OP_CODE_PARA_ERR,retCode);
    return;
    }
    if (cmd_code != NULL && cJSON_IsString(cmd_code)) {
    _code = cJSON_GetStringValue(cmd_code);      
    } else {
        strcpy(retCode,OP_CODE_PARA_ERR);
    // copy_const_chars(OP_CODE_PARA_ERR,retCode);
    return;
    }
    const char * resp=km_start_ble(_nick,_code,"cnjoypad.net", "funny, smiling, hid device",true);
    // copy_const_chars(resp,retCode);
    strcpy(retCode,resp);
}

void _handle_hid_touch(char * cmdType,cJSON *cmdJson,char * retCode){
    if(cmdJson==NULL){
        strcpy(retCode,OP_CODE_PARA_ERR);
        // copy_const_chars(OP_CODE_PARA_ERR,retCode);
        return;
    }
    ESP_LOGI(TAG,"handle - %s",cmdType);
     cJSON *itm_finger = cJSON_GetObjectItem(cmdJson, "finger");
     cJSON *itm_x = cJSON_GetObjectItem(cmdJson, "x");
     cJSON *itm_y = cJSON_GetObjectItem(cmdJson, "y");

     uint8_t finger;
     uint16_t x;
     uint16_t y;
     if(itm_finger==NULL||itm_x==NULL||itm_y==NULL){
        strcpy(retCode,OP_CODE_PARA_ERR);
        // copy_const_chars(OP_CODE_PARA_ERR,retCode);
        return;
     }

    finger=cJSON_GetNumberValue(itm_finger);
    x=cJSON_GetNumberValue(itm_x);
    y=cJSON_GetNumberValue(itm_y);

    const char * resp=hid_mobile_touch(finger,x,y);
    strcpy(retCode,resp);
    // copy_const_chars(resp,retCode);
}
void _handle_hid_release(char * cmdType,cJSON *cmdJson,char * retCode){
    if(cmdJson==NULL){
        strcpy(retCode,OP_CODE_PARA_ERR);
        // copy_const_chars(OP_CODE_PARA_ERR,retCode);
        return;
    }
    ESP_LOGI(TAG,"handle - %s",cmdType);
     cJSON *itm_finger = cJSON_GetObjectItem(cmdJson, "finger");
     uint8_t finger;
     if(itm_finger==NULL){
        strcpy(retCode,OP_CODE_PARA_ERR);
        // copy_const_chars(OP_CODE_PARA_ERR,retCode);
        return;
     }

    finger=cJSON_GetNumberValue(itm_finger);
    
    const char * resp=hid_mobile_release(finger);
    // copy_const_chars(resp,retCode);
    strcpy(retCode,resp);
}
void _handle_hid_tap(char * cmdType,cJSON *cmdJson,char * retCode){
    if(cmdJson==NULL){
        strcpy(retCode,OP_CODE_PARA_ERR);
        // copy_const_chars(OP_CODE_PARA_ERR,retCode);
        return;
    }
    ESP_LOGI(TAG,"handle - %s",cmdType);
     cJSON *itm_finger = cJSON_GetObjectItem(cmdJson, "finger");
     cJSON *itm_x = cJSON_GetObjectItem(cmdJson, "x");
     cJSON *itm_y = cJSON_GetObjectItem(cmdJson, "y");

     uint8_t finger;
     uint16_t x;
     uint16_t y;
     if(itm_finger==NULL||itm_x==NULL||itm_y==NULL){
        strcpy(retCode,OP_CODE_PARA_ERR);
        // copy_const_chars(OP_CODE_PARA_ERR,retCode);
        return;
     }

    finger=cJSON_GetNumberValue(itm_finger);
    x=cJSON_GetNumberValue(itm_x);
    y=cJSON_GetNumberValue(itm_y);

    const char * resp=hid_mobile_tap(finger,x,y);
    // copy_const_chars(resp,retCode);
    strcpy(retCode,resp);
}
//--蓝牙+hid end
